/*
Copyright 2012 Gary Bortosky

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;

namespace Bortosky.UriQueryTranslation
{
    /// <summary>
    /// The parts of a relative or absolute URI
    /// </summary>
    public class UriParts
    {
        private static char _amper = '&';
        private static char _qmark = '?';
        private static char _hmark = '#';
        private static char[] _ampersplit = new char[] { _amper };

        public string Path { get; set; }
        public string Query { get; set; }
        public string Fragment { get; set; }

        /// <summary>
        /// Constructs an instance
        /// </summary>
        public UriParts()
        {
        }

        /// <summary>
        /// Constructs an instance
        /// </summary>
        /// <param name="path">The path part of the address</param>
        /// <param name="query">The query part of the address</param>
        /// <param name="fragment">The fragment part of the address</param>
        public UriParts(string path, string query, string fragment)
        {
            Path = path;
            Query = query;
            Fragment = fragment;
        }

        /// <summary>
        /// Attempts to parse the given address to an instance
        /// </summary>
        /// <param name="address">The address to parse</param>
        /// <param name="results">The UriPart if the parse is successful</param>
        /// <returns>true if the string was successfully parsed in results</returns>
        public static bool TryParse(string address, out UriParts results)
        {
            try
            {
                results = Parse(address);
                return true;
            }
            catch
            {
                results = null;
            }
            return false;
        }

        /// <summary>
        /// Parses the supplied address into an instance
        /// </summary>
        /// <param name="address">The address to parse</param>
        /// <returns>The instance corresponding to the address</returns>
        public static UriParts Parse(string address)
        {
            if (address == null || (address.Count(g => g.Equals(_qmark)) > 1) || (address.Count(g => g.Equals(_hmark)) > 1))
            {
                throw new FormatException();
            }

            var q = address.IndexOf(_qmark);
            var h = address.IndexOf(_hmark);
            var l = address.Length;

            if (h > -1 && q > h)
            {
                throw new FormatException();
            }

            return new UriParts
            {
                Path = address.Substring(0, q > -1 ? q : (h > -1 ? h : l)),
                Query = q > -1
                    ? address.Substring(q + 1, (h > -1 ? h : l) - (q + 1))
                    : null,
                Fragment = h > -1
                    ? address.Substring(h + 1, l - (h + 1))
                    : null
            };
        }

        /// <summary>
        /// Parses this instance's Query part
        /// </summary>
        /// <returns>A sequence of KeyValuePair instances</returns>
        public IEnumerable<KeyValuePair<String, String>> QueryKeyValuePairs()
        {
            return QueryKeyValuePairs(Query);
        }

        /// <summary>
        /// Parses a query part
        /// </summary>
        /// <returns>A sequence of KeyValuePair instances</returns>
        public static IEnumerable<KeyValuePair<String, String>> QueryKeyValuePairs(string query)
        {
            if (query == null || query.Contains(_hmark) || query.Contains(_qmark))
            {
                throw new FormatException();
            }
            foreach (var keqv in query.Split(_ampersplit, StringSplitOptions.RemoveEmptyEntries))
            {
                var kv = keqv.Split('=');
                yield return new KeyValuePair<String, String>(kv.ElementAt(0).Trim(), kv.Length.Equals(2) ? kv.ElementAt(1).Trim() : null);
            }
        }

        /// <summary>
        /// Produces a string representation of this instance
        /// </summary>
        /// <returns>a string representation of this instance</returns>
        public override string ToString()
        {
            var s = Path;
            s += Query == null ? string.Empty : "?" + Query;
            s += Fragment == null ? string.Empty : "#" + Fragment;
            return s;
        }
    }
}